package com.dsg.workflow;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.dsg.common.util.FileNotCompleteException;
import com.dsg.common.util.UUIDGenerator;
import com.dsg.common.util.toolbox.CollectionUtil;
import com.dsg.common.util.toolbox.StringUtil;
import com.dsg.entity.apply.DsgApplyBill;
import com.dsg.entity.apply.DsgApplyCar;
import com.dsg.entity.apply.DsgApplyCard;
import com.dsg.entity.approve.DsgApproveCase;
import com.dsg.entity.approve.DsgApproveNode;
import com.dsg.entity.approve.DsgApproveTask;
import com.dsg.entity.setting.SsoUser;
import com.dsg.service.apply.*;
import com.dsg.service.approve.IDsgApproveCaseService;
import com.dsg.service.approve.IDsgApproveNodeService;
import com.dsg.service.approve.IDsgApproveProcessService;
import com.dsg.service.approve.IDsgApproveTaskService;
import com.dsg.service.setting.ISsoUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public abstract class AbstractWorkflowService {
    public static final String NODE_CS_APPLY = "CS入機";
    public static final String NODE_CS_WAITING_LIST = "CS Waiting List";
    public static final String NODE_INPUT_CAR_INFO = "錄入車輛資料";
    public static final String NODE_DEDUCT_ONE = "過1元";
    public static final String NODE_RETURN_CARD = "返咭";
    public static final String NODE_DDA = "會計部設立DDA";
    public static final String NODE_CHECK_LIST = "客服中心對單";
    public static final String NODE_SEND_CARD = "出咭";
    public static final String NODE_SUP_APPROVE = "上級批核";
    public static final String NODE_UPLOAD_FORM = "上Form/Online";
    public static final String NODE_CHECK_CAR_NO = "Check車牌";

    @Autowired
    IDsgApproveTaskService taskService;
    @Autowired
    IDsgApproveProcessService processService;
    @Autowired
    IDsgApproveNodeService nodeService;
    @Autowired
    IDsgApproveCaseService caseService;
    @Autowired
    ISsoUserService userService;
    @Autowired
    IDsgApplyBillService billService;
    @Autowired
    IDsgApplyPayService payService;
    @Autowired
    WorkflowEngine engine;
    @Autowired
    IDsgApplyStatusService statusService;
    @Autowired
    IDsgApplyCardService cardService;
    @Autowired
    IDsgApplyCarService carService;

    /**
     * 動態判斷前進節點
     * @return
     */
    List<String> determineForwardNodeIdList(DsgApproveCase approveCase, String nodeName) throws Exception {
        List<String> list = new ArrayList<>();
        switch (nodeName) {
            case NODE_CS_APPLY:
                try {
                    engine.fileComplete(approveCase);
                    list.add(NODE_INPUT_CAR_INFO);
                } catch (FileNotCompleteException e) {
                    list.add(NODE_CS_WAITING_LIST);
                }
                break;
        }

//        if (CollectionUtil.isEmpty(list)) {
//            throw new Exception(String.format("節點%s無後節點，請檢查配置", nodeName));
//        }

        return list;
    }

    public void begin(DsgApplyBill bill) throws Exception {
        DsgApproveCase approveCase = caseService.getBeginCase(bill);
        if (approveCase != null) {
            log.info("已生成過開始實例");
            return;
        }
        DsgApproveNode node = nodeService.getBeginNode(bill);
        generateCases(null, null, node.getNodeName(), bill);
    }


    private boolean isDeduOneCase(DsgApproveCase approveCase) {
        DsgApproveTask task = taskService.getById(approveCase.getTaskId());
        if (NODE_DEDUCT_ONE.equals(task.getTaskName())) {
            return true;
        } else {
            return false;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void forward(DsgApproveCase approveCase) throws Exception {
        //若存在已開始和已審批的case则不生成case
//        QueryWrapper wrap = new QueryWrapper();
//        wrap.eq("APPLY_ID", approveCase.getApplyId());
//        wrap.eq("TASK_ID", approveCase.getTaskId());
//        wrap.in("STATUS", DsgApproveCase.Status.started.getCode(), DsgApproveCase.Status.approved.getCode());
//        if (caseService.count(wrap) != 0) {
//            return;
//        }
//        approveCase = caseService.getById(approveCase.getOid());
        if (approveCase == null)
            new Exception("CASE為空");

        //針對重新過一元的情況
        if (isDeduOneCase(approveCase)) {
            DsgApproveCase preDeduOneCase = caseService.getDeduOneCase(approveCase.getAppNo());
            if (preDeduOneCase != null) {
                approveCase(approveCase);
                return;
            }
        }

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("APP_NO", approveCase.getAppNo());
        DsgApplyBill bill = billService.getOne(wrapper);

        if (approveCase == null) {
            throw new Exception("CASE已不存在");
        } else if (!approveCase.getStatus().equals(DsgApproveCase.Status.started.getCode())) {
            throw new Exception("CASE已审批");
        }

        DsgApproveTask task = taskService.getById(approveCase.getTaskId());
        DsgApproveNode currentNode = nodeService.getById(task.getNodeId());

//        if (currentNode.getNodeType().equals(DsgApproveNode.NodeType.END.name())) {
//            return;
//        }

        List<DsgApproveNode> brotherNodeList = getBrotherNodeList(currentNode, bill);
        if (CollectionUtil.isNotEmpty(brotherNodeList)) {
            approveCase(approveCase);
            if (!brotherApproved(brotherNodeList, bill))
                return;//直接返回，不產生下級case
        }

        boolean allGenerated = true;
        //产生下级case
        if (!currentNode.getNodeType().equals(DsgApproveNode.NodeType.END.name())) {
            String forwardIds = currentNode.getForwardNode();
            List<String> forwardIdList = new ArrayList<>();
            List<String> forwardNodeNameList = new ArrayList<>();
            if (StringUtil.isNotEmpty(forwardIds)) {
                if (forwardIds.contains(",")) {
                    forwardIdList.addAll(Arrays.asList(forwardIds.split(",")));
                } else {
                    forwardIdList.add(forwardIds);
                }
            } else {
                forwardNodeNameList.addAll(determineForwardNodeIdList(approveCase, currentNode.getNodeName()));
            }
            forwardIdList.forEach(e -> {
                DsgApproveNode node = nodeService.getById(e);
                forwardNodeNameList.add(node.getNodeName());
            });
            allGenerated = generateCasesByNodeNames(currentNode.getNodeName(), currentNode.getOid(), forwardNodeNameList, bill);
        }

        if (allGenerated && CollectionUtil.isEmpty(brotherNodeList)) {
            approveCase(approveCase);
        }
    }

    /**
     * 針對改變付款資料就重新過一元的情況，重新生成實例
     */
    public void generateDeduOneCases(DsgApplyBill bill) throws Exception {
        DsgApproveCase deduOneCase = caseService.getDeduOneCase(bill.getAppNo());
        if (deduOneCase != null) {
            generateCases(null, deduOneCase.getNodeId(), NODE_DEDUCT_ONE, bill);
        }
    }

    private List<DsgApproveNode> getBrotherNodeList(DsgApproveNode currentNode, DsgApplyBill bill) {
        List<DsgApproveNode> brotherNodeList = null;
        List<DsgApproveNode> list = new ArrayList<>();
        if (StringUtil.isNotEmpty(currentNode.getForwardNode())) {
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.select("OID oid, NODE_NAME nodeName");
            wrapper.eq("FORWARD_NODE", currentNode.getForwardNode());
            wrapper.ne("OID", currentNode.getOid());
            brotherNodeList = nodeService.list(wrapper);
            for (DsgApproveNode node : brotherNodeList) {
                if (needNode(currentNode.getNodeName(), bill, node.getNodeName()))
                    list.add(node);
            }
        }
        return list;
    }

    /**
     * 令CASE通過
     * @param approveCase
     */
    void approveCase(DsgApproveCase approveCase) {
        approveCase.setStatus(DsgApproveCase.Status.approved.getCode());
        caseService.updateById(approveCase);
        deleteCaseList(approveCase);

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("OID", approveCase.getTaskId());
        DsgApproveTask task = taskService.getOne(wrapper);
        switch (task.getTaskName()) {
            case NODE_DEDUCT_ONE:
                deductOneFillBack(approveCase);
                break;
            case NODE_DDA:
                ddaFillBack(approveCase);
                break;
            case NODE_CHECK_LIST:
                checkListFillBack(approveCase);
                break;
            case NODE_SUP_APPROVE:
                approveFillBack(approveCase);
                break;
            case NODE_UPLOAD_FORM:
                uploadFormFillBack(approveCase);
                break;
            case NODE_SEND_CARD:
                sendCardFillBack(approveCase);
                break;
        }
    }

    public void ddaFillBack(DsgApproveCase approveCase) {
    }

    public void checkListFillBack(DsgApproveCase approveCase) {
    }

    public void deductOneFillBack(DsgApproveCase approveCase) {
    }

    public void approveFillBack(DsgApproveCase approveCase) {
    }

    public void uploadFormFillBack(DsgApproveCase approveCase) {
    }

    public void sendCardFillBack(DsgApproveCase approveCase) {
    }

    protected DsgApplyBill getBillByAppNo(String appNo) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("APP_NO", appNo);
        DsgApplyBill bill = billService.getOne(wrapper);
        return bill;
    }

    /**
     * 兄弟结点是否全部审批完毕(forwardNode相同的为兄弟节点)
     * @param brotherNodeList
     * @param bill
     * @return
     */
    private boolean brotherApproved(List<DsgApproveNode> brotherNodeList, DsgApplyBill bill) {
        for (DsgApproveNode node : brotherNodeList) {
            DsgApproveCase caseByNode = caseService.getApprovedCaseByNode(node.getOid(), bill.getAppNo());
            if (caseByNode != null) {
                return false;
            }
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public void backward(DsgApproveCase approveCase) throws Exception {
        if (caseService.getById(approveCase.getOid()) == null) {
            throw new Exception("CASE已不存在");
        }
        approveCase.setStatus(DsgApproveCase.Status.undo.getCode());
        caseService.updateById(approveCase);
        deleteCaseList(approveCase);

        //todo 如果有同輩結點(backwardNode相同的結點)，要刪除同輩結點的case

        DsgApproveNode node = nodeService.getNodeByCase(approveCase.getOid());
        String backwardNodeId = node.getBackwardNode();
        if (StringUtil.isEmpty(backwardNodeId)) {
            backwardNodeId = approveCase.getNodeId();
        }

        List<String> backwardNodeList = new ArrayList<>();
        if (backwardNodeId.contains(",")) {
            backwardNodeList = Arrays.asList(backwardNodeId.split(","));
        } else {
            backwardNodeList.add(backwardNodeId);
        }

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("APP_NO", approveCase.getAppNo());
        DsgApplyBill bill = billService.getOne(wrapper);
        for (String backNodeId : backwardNodeList) {
            DsgApproveCase approveCase1 = caseService.getCase(backNodeId, approveCase.getAppNo());
            DsgApproveNode node1 = nodeService.getById(backNodeId);
            generateCases(null, approveCase1.getNodeId(), node1.getNodeName(), bill);
        }
    }

    /**
     * 有一个CASE已被审批，则删除其它CASE
     * @param approveCase
     */
    private void deleteCaseList(DsgApproveCase approveCase) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("TASK_ID", approveCase.getTaskId());
        wrapper.eq("APP_NO", approveCase.getAppNo());
        wrapper.ne("OID", approveCase.getOid());
        caseService.remove(wrapper);
    }

    private boolean generateCasesByNodeNames(String currNodeName, String backwardNodeId, List<String> nodeNames, DsgApplyBill bill) throws Exception {
        if (CollectionUtil.isEmpty(nodeNames)) {
            return true;
        }

        boolean allGenerated = true;
        for (String nodeName : nodeNames) {
//            if (nodeName.equals(WorkflowService3.NODE_DDA) && !needDda(approveCase.getAppNo()))
//                continue;
            allGenerated = generateCases(currNodeName, backwardNodeId, nodeName, bill) && allGenerated;
        }
        return allGenerated;
    }

    protected boolean canGoOn(DsgApplyBill bill) throws Exception {
        return true;
    }

    /**
     * 獲取需要根據條件推進的節點，如內審通過才生成的CASE
     * @return
     */
    protected Set getConditionalNodeSet(DsgApplyBill bill) {
        return new HashSet();
    }

//    private boolean needDda(String appNo) {
//        QueryWrapper wrapper = new QueryWrapper();
//        wrapper.select("MAIN_NO");
//        wrapper.eq("APP_NO", appNo);
//        List<DsgApplyBill> billList = billService.list(wrapper);
//        if (CollectionUtil.isNotEmpty(billList)) {
//            DsgApplyBill bill = billList.get(0);
//            wrapper = new QueryWrapper();
//            wrapper.eq("MAIN_NO", bill.getMainNo());
//            wrapper.in("PAY_MODE", DsgApplyPay.PAY_MODE_DAY_ATP, DsgApplyPay.PAY_MODE_HALF_MONTH_ATP,
//                    DsgApplyPay.PAY_MODE_MONTH_ATP, DsgApplyPay.PAY_MODE_WEEK_ATP);
//            if (payService.count(wrapper) > 0)
//                return true;
//            else
//                return false;
//        }
//
//        return false;
//    }

    public void genByNodeName(String nodeName, DsgApplyBill bill) throws Exception {
        DsgApproveNode node = nodeService.getNode(bill.getAppNo(), nodeName);
        generateCases(null, node.getBackwardNode(), node.getNodeName(), bill);
    }

    private boolean generateCases(String currNodeName, String backwardNodeId, String nodeName, DsgApplyBill bill) throws Exception {
        if (!needNode(currNodeName, bill, nodeName))
            return true;

        String appNo = bill.getAppNo();
        if (getConditionalNodeSet(bill).contains(nodeName)) {
            boolean canGoOn = canGoOn(bill);
            if (!canGoOn) {
                return false;
            }
        }

//        QueryWrapper wrapper = new QueryWrapper();
//        wrapper.eq("TASK_NAME", nodeName);
        DsgApproveTask task = taskService.getTask(bill.getAppNo(), nodeName, bill.getEntCode());

        List<SsoUser> userList = new ArrayList<>();
        if (DsgApproveTask.RbacType.user.getCode().equals(task.getRbacType())) {
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("USER_NAME", task.getRbacId());
//            SsoUser user = userService.getById(task.getRbacId());
            SsoUser user = userService.getOne(wrapper);
            userList.add(user);
        } else if (DsgApproveTask.RbacType.org.getCode().equals(task.getRbacType())) {
            userList.addAll(userService.getUserByOrg(task.getRbacId()));
        } else if (DsgApproveTask.RbacType.role.getCode().equals(task.getRbacType())) {
            userList.addAll(userService.getUserByRole(task.getRbacId()));
        }

        //若存在case则不生成case
        QueryWrapper wrap = new QueryWrapper();
        wrap.eq("APP_NO", appNo);
        wrap.eq("TASK_ID", task.getOid());
//        wrap.in("STATUS", DsgApproveCase.Status.started.getCode(), DsgApproveCase.Status.approved.getCode());
        if (caseService.count(wrap) != 0) {
            return true;
        }

        List<DsgApproveCase> caseList = new ArrayList<>();
        userList.forEach(u -> {
            DsgApproveCase approveCase = new DsgApproveCase();
            approveCase.setOid(UUIDGenerator.getUUID());
            approveCase.setAppNo(appNo);
            approveCase.setTaskId(task.getOid());
            approveCase.setUserId(u.getUserName());
            approveCase.setStatus(DsgApproveCase.Status.started.getCode());
            approveCase.setNodeId(backwardNodeId);
            approveCase.setEntCode(bill.getEntCode());
            caseList.add(approveCase);
        });

        caseService.saveBatch(caseList);
        return true;
    }

    protected boolean needNode(String currNodeName, DsgApplyBill bill, String nodeName) {
        return true;
    }

    /**
     * 四個月是否有成功扣款記錄
     * @return
     */
    public boolean lastSuccess(DsgApplyBill bill) {
        Integer value = payService.successInFourMonth(bill);
        if (value != null && value == 1)
            return true;
        else
            return false;
    }

    /**
     * 轉移油咭正式生效，變更數據狀態
     */
    @Transactional(rollbackFor = Exception.class)
    void tranValidate(String appNo) {
        log.info("appNo:{}, 轉移油咭正式生效，變更數據狀態", appNo);
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.select("CAR_NO", "OID");
        wrapper.eq("APP_NO", appNo);
        List<DsgApplyCar> carList = carService.list(wrapper);
        carList.forEach(e -> e.setCarStatus(DsgApplyCar.CAR_STATUS_EFFECT));
        carService.updateBatchById(carList);

        List<String> carNoList = carList.stream().map(e -> e.getCarNo()).collect(Collectors.toList());
        LambdaUpdateWrapper<DsgApplyCar> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(DsgApplyCar::getCarNo, carNoList);
        updateWrapper.ne(DsgApplyCar::getAppNo, appNo);
        DsgApplyCar car = new DsgApplyCar();
        car.setCarStatus(DsgApplyCar.CAR_STATUS_TRAN);
        carService.update(car, updateWrapper);

        wrapper = new QueryWrapper();
        wrapper.select("CARD_NO", "OID");
        wrapper.eq("APP_NO", appNo);
        List<DsgApplyCard> cardList = cardService.list(wrapper);
        cardList.forEach(e -> e.setCardStatus(DsgApplyCard.CARD_STATUS_4));
        cardService.updateBatchById(cardList);

        List<String> cardNoList = cardList.stream().map(e -> e.getCardNo()).collect(Collectors.toList());
        LambdaUpdateWrapper<DsgApplyCard> updateWrapper1 = new LambdaUpdateWrapper<>();
        updateWrapper1.in(DsgApplyCard::getCardNo, cardNoList);
        updateWrapper1.ne(DsgApplyCard::getAppNo, appNo);
        DsgApplyCard card = new DsgApplyCard();
        card.setCardStatus(DsgApplyCard.CARD_STATUS_9);
        cardService.update(card, updateWrapper1);
    }

    protected String recalculateNodeName(DsgApplyBill bill) {
        return NODE_CHECK_CAR_NO;
    }

    /**
     * 重新運算生成CASE
     */
    public void recalculate(DsgApplyBill bill) throws Exception {
        genByNodeName(recalculateNodeName(bill), bill);
    }
}
